﻿#include "dxstdafx.h"
#include "CStrategyRenderCommon.h"
#include "../CStrategyDefine.h"
#include "Data/DataMgr.h"
#include "Data/ProgramData/ProgramDataMgr.h"
#include "Data/ProgramData/StrategyMapData.h"
#include "Data/ProgramData/ProvinceData.h"
#include "Support/FNTFontReader/fnt.hpp"

extern CStrategyCamera g_Camera;
extern CStrategyRenderData g_StrategyRenderData;

void CStrategyRenderData::OnFrameMove(const D3DVIEWPORT9* pViewPort)
{
	m_ViewPort = *pViewPort;
}

void CStrategyRenderData::OnDestroyDevice()
{
	SAFE_RELEASE(m_pTexTerrainDiffuse);
	SAFE_RELEASE(m_pTexHeightNormal);
	SAFE_RELEASE(m_pTexTerrainColorTint);
	SAFE_RELEASE(m_pTexTerrainNormal);
	SAFE_RELEASE(m_pTexTerrainIDMap);

	SAFE_RELEASE(m_pTexRiverSurfaceDiffuse);
	SAFE_RELEASE(m_pTexRiverSurfaceNormal);
	SAFE_RELEASE(m_pTexRiverBottomDiffuse);
	SAFE_RELEASE(m_pTexRiverBottomNormal);

	SAFE_RELEASE(m_pTexBorderDiffuse);
}

bool CStrategyRenderData::UpdateRenderSquare(std::set<int>* set_RenderSquareOut)
{
	CHECK_RE_FALSE(set_RenderSquareOut);
	int nSquareRowMax, nSquareLineMax;
	if (!g_StrategyRenderData.GetRenderAreaWH(&nSquareRowMax, &nSquareLineMax))
	{
		return false;
	}

	const BYTE* pHeightBuf = CDataMgr::Singleton().GetProgramDataMgrCPtr()->GetStrategyMapDataCPtr()->GetHeightBufCPtr();

	auto World2Screen = [this, pHeightBuf](D3DXVECTOR3* pOut, int WorldX, int WorldY) {
		int indexHeight = CMathFunc::XYTran2Index(WorldX, WorldY, c_nTerrainWidth, c_nTerrainHeight);
		D3DXVECTOR3 pos(WorldX * c_fTerrainSqareScale, pHeightBuf[indexHeight] * c_fHeightMapScale, WorldY * c_fTerrainSqareScale);
		D3DXVec3Project(pOut, &pos, &this->m_ViewPort, g_Camera.GetProjMatrix(), g_Camera.GetViewMatrix(), &g_mWorld);		// 世界坐标转换成屏幕坐标
	};

	for (int iH = 0; iH < nSquareLineMax; ++iH)
	{
		for (int jW = 0; jW < nSquareRowMax; ++jW)
		{
			int WorldX = jW * c_nTerrainSquareSize;
			int WorldY = iH * c_nTerrainSquareSize;
			D3DXVECTOR3 ptInScreen;
			World2Screen(&ptInScreen, WorldX, WorldY);		// 世界坐标转换成屏幕坐标
			if (m_ViewPort.X <= ptInScreen.x && ptInScreen.x <= (m_ViewPort.X + m_ViewPort.Width) &&
				m_ViewPort.Y <= ptInScreen.y && ptInScreen.y <= (m_ViewPort.Y + m_ViewPort.Height))			// 为了在分屏显示时也不会出错
			{
				int indexLD = CMathFunc::XYTran2Index((0 == jW ? 0 : jW - 1), (0 == iH ? 0 : iH - 1), nSquareRowMax, nSquareLineMax); // 左下
				set_RenderSquareOut->insert(indexLD);
				int indexLU = CMathFunc::XYTran2Index((0 == jW ? 0 : jW - 1), iH, nSquareRowMax, nSquareLineMax); // 左上
				set_RenderSquareOut->insert(indexLU);
				int indexRD = CMathFunc::XYTran2Index(jW, (0 == iH ? 0 : iH - 1), nSquareRowMax, nSquareLineMax); // 右下
				set_RenderSquareOut->insert(indexRD);
				int indexRU = CMathFunc::XYTran2Index(jW, iH, nSquareRowMax, nSquareLineMax); // 右上
				set_RenderSquareOut->insert(indexRU);
			}
		}
	}
	m_set_RenderSquare = *set_RenderSquareOut;
	if (set_RenderSquareOut->empty())
	{
		return false;
	}
	return true;
}

bool CStrategyRenderData::GetRenderSquareSet(std::set<int>* set_RenderSquareOut) const
{
	CHECK_RE_FALSE(set_RenderSquareOut);
	if (m_set_RenderSquare.empty())
	{
		return false;
	}
	*set_RenderSquareOut = m_set_RenderSquare;
	return true;
}

bool CStrategyRenderData::GetRenderAreaWH(int* pWidth, int* pHeight)
{
	if (0 == m_nRenderAreaWidth || 0 == m_nRenderAreaHeight)
	{
		if (0 != c_nTerrainWidth % c_nTerrainSquareSize)
		{
			CString strError;
			strError.Format(L"%d不能被%d整除", c_nTerrainWidth, c_nTerrainSquareSize);
			CommonFunc::ErrorMessageBox(strError);
			return false;
		}
		if (0 != c_nTerrainHeight % c_nTerrainSquareSize)
		{
			CString strError;
			strError.Format(L"%d不能被%d整除", c_nTerrainHeight, c_nTerrainSquareSize);
			CommonFunc::ErrorMessageBox(strError);
			return false;
		}
		*pWidth = c_nTerrainWidth / c_nTerrainSquareSize;
		*pHeight = c_nTerrainHeight / c_nTerrainSquareSize;
		m_nRenderAreaWidth = *pWidth;
		m_nRenderAreaHeight = *pHeight;
	}
	else
	{
		*pWidth = m_nRenderAreaWidth;
		*pHeight = m_nRenderAreaHeight;
	}
	return true;
}

HRESULT CStrategyRenderData::CreateRenderTexture(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	std::wstring szRcPathOut;
	D3DXIMAGE_INFO image_info;
	
	{	// 装入纹理
		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\atlas0.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));	// 读文件信息
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexTerrainDiffuse));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\world_normal_height.bmp", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexHeightNormal));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\colormap.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexTerrainColorTint));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\atlas_normal0.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexTerrainNormal));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\RiverSurface_diffuse.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexRiverSurfaceDiffuse));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\RiverSurface_normal.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexRiverSurfaceNormal));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\Riverbottom_Diffuse.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexRiverBottomDiffuse));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\River_normal.bmp", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexRiverBottomNormal));

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\border0.dds", szRcPathOut);
		V_RETURN(D3DXGetImageInfoFromFile(szRcPathOut.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, szRcPathOut.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexBorderDiffuse));
	}

	{	// 通过转化过的m_pTerrainID生成纹理
		RGBQUAD* pColorTable = CommonFunc::Get8BitColorTable();
		std::wstring wstrPersonalPath;
		CommonFunc::GetPersonalPathCacheDirectory(wstrPersonalPath);
		wstrPersonalPath.append(L"\\TerrainByte.bmp");
		CommonFunc::Write8BitBmp(wstrPersonalPath, m_pTerrainID.get(), pColorTable, c_nTerrainWidth, c_nTerrainHeight);
		V_RETURN(D3DXGetImageInfoFromFile(wstrPersonalPath.c_str(), &image_info));
		V_RETURN(D3DXCreateTextureFromFileEx(pd3dDevice, wstrPersonalPath.c_str(), image_info.Width, image_info.Height,
			image_info.MipLevels, 0, image_info.Format, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			NULL, NULL, &m_pTexTerrainIDMap));
		DeleteFile(wstrPersonalPath.c_str());
	}
	return hr;
}

bool CStrategyRenderData::TransformTerrainMap(const BYTE* pTerrainID)
{
	CHECK_RE_FALSE(pTerrainID);

	{
		BYTE* temp_pTerrainID = new BYTE[c_nTerrainWidth * c_nTerrainHeight];
		DWORD max_size = sizeof(BYTE) * c_nTerrainWidth * c_nTerrainHeight;
		memcpy_s(temp_pTerrainID, max_size, pTerrainID, max_size);
		m_pTerrainID.reset(temp_pTerrainID);
	}	

	// 若上下左右ID一致，则+100
	for (int i = 0; i < c_nTerrainWidth * c_nTerrainHeight; ++i)
	{
		int x, y, j = DirMin;
		CMathFunc::IndexTran2XY(&x, &y, i, c_nTerrainWidth, c_nTerrainHeight);
		for (; j < DirMax; ++j)
		{
			int index;
			switch ((_eDirULDR)j)
			{
			case Up:
				index = CMathFunc::XYTran2Index(x, y + 1, c_nTerrainWidth, c_nTerrainHeight);
				break;
			case Left:
				index = CMathFunc::XYTran2Index(x - 1, y, c_nTerrainWidth, c_nTerrainHeight);
				break;
			case Down:
				index = CMathFunc::XYTran2Index(x, y - 1, c_nTerrainWidth, c_nTerrainHeight);
				break;
			case Right:
				index = CMathFunc::XYTran2Index(x + 1, y, c_nTerrainWidth, c_nTerrainHeight);
				break;
			default:
				return false;
				break;
			}
			if (i != index)
			{
				if (m_pTerrainID[index] < 100)
				{
					if (m_pTerrainID[i] != m_pTerrainID[index])
					{
						break;
					}
				}
				else if (m_pTerrainID[i] != m_pTerrainID[index] - 100)
				{
					break;
				}
			}
		}
		if (4 == j)
		{
			m_pTerrainID[i] += 100;
		}
	}
	return true;
}

void CStrategyRenderFunc::GetTerrainXYByScreenPos(float fScreenX, float fScreenY, CPoint& ptOut)
{
	// 左下角为 (-1, -1)
	// 计算射线
	int nScreenWidth = 0, nScreenHeight = 0;
	CommonFunc::GetScreenWidthnHeight(&nScreenWidth, &nScreenHeight);
	float fFOV = g_Camera.GetFOV();
	D3DXVECTOR3 vecRay(fScreenX * tan(fFOV / 2.0f) * nScreenWidth / nScreenHeight, fScreenY * tan(fFOV / 2.0f), 1.0f); // 射线
	D3DXVec3TransformNormal(&vecRay, &vecRay, g_Camera.GetWorldMatrix()); // 向量变换

	float t = (round(c_nSeaLevelHeight * c_fHeightMapScale) - g_Camera.GetEyePt()->y) / vecRay.y;
	CPoint CrossPoint;
	CrossPoint.x = round((g_Camera.GetEyePt()->x + t * vecRay.x) / c_fTerrainSqareScale);
	CrossPoint.y = round((g_Camera.GetEyePt()->z + t * vecRay.z) / c_fTerrainSqareScale);
	ptOut = CrossPoint;
}

int CStrategyRenderFunc::GetRenderSquareIndex(float x, float y)
{
	int SquareRowMax, SquareLineMax;
	g_StrategyRenderData.GetRenderAreaWH(&SquareRowMax, &SquareLineMax);

	int nRow = CMathFunc::Saturate((int)floor(x / c_nTerrainSquareSize), 0, SquareRowMax - 1);
	int nLine = CMathFunc::Saturate((int)floor(y / c_nTerrainSquareSize), 0, SquareLineMax - 1);

	return CMathFunc::XYTran2Index(nRow, nLine, SquareRowMax, SquareLineMax);
}

void CStrategyRenderFunc::GetNameSumWidthNHeightInFNT(float& SumWidthOut, float& SumHeightOut, const std::wstring& name, const FontCharSet* pFntFace, BYTE SpaceWidth /*= 0*/)
{
	SumWidthOut = 0;
	SumHeightOut = 0;
	CHECK_RETURN(pFntFace);
	for (auto i : name)
	{
		const FontChar* pFontChar = pFntFace->GetChar(i);
		CHECK_CONTINUE(pFontChar);
		SumWidthOut += pFontChar->width;
		SumWidthOut += SpaceWidth;
		SumHeightOut += pFontChar->height;
		SumHeightOut += SpaceWidth;
	}
	SumWidthOut -= SpaceWidth;
	SumHeightOut -= SpaceWidth;
}

void CStrategyRenderFunc::GetNameMiddleLine(D3DXVECTOR2& pointStartOut, D3DXVECTOR2& DirectionVectorOut, D3DXVECTOR2& NormalVectorOut, bool& bLineVerticalOut, const D3DXVECTOR2& pointA, const D3DXVECTOR2& pointB, DWORD SumWidthIn, DWORD SumHeightIn)
{
	D3DXVECTOR2 midPoint = (pointA + pointB) / 2.f;
	DirectionVectorOut = pointA - pointB;
	D3DXVec2Normalize(&DirectionVectorOut, &DirectionVectorOut);

	if (abs(D3DXVec2Dot(&DirectionVectorOut, &D3DXVECTOR2(1.f, 0.f))) < sqrt(2) / 2.f)	// 与X轴 45度到135度 之间
	{
		bLineVerticalOut = true;
		// 从上到下
		if (DirectionVectorOut.y > 0)
		{
			DirectionVectorOut = -DirectionVectorOut;
		}
		NormalVectorOut = D3DXVECTOR2(-DirectionVectorOut.y, DirectionVectorOut.x);	// 从字的左上角 指向 右上角

		pointStartOut = midPoint - DirectionVectorOut * SumHeightIn / 2.f;
	}
	else
	{
		bLineVerticalOut = false;
		// 从左到右
		if (DirectionVectorOut.x < 0)
		{
			DirectionVectorOut = -DirectionVectorOut;
		}
		NormalVectorOut = D3DXVECTOR2(DirectionVectorOut.y, -DirectionVectorOut.x);	// 从字的左上角 指向 左下角

		pointStartOut = midPoint - DirectionVectorOut * SumWidthIn / 2.f;
	}
}

int CStrategyRenderFunc::GetTerrainIndexBFloatXY(float x, float y)
{
	x = CMathFunc::Saturate(x, 0.f, (float)c_nTerrainWidth - 1);
	y = CMathFunc::Saturate(y, 0.f, (float)c_nTerrainHeight - 1);

	return CMathFunc::XYTran2Index(x, y, c_nTerrainWidth, c_nTerrainHeight);
}

float CStrategyRenderFunc::GetHeightByFloatXY(float x, float y)
{
	const BYTE* pHeightBuf = CDataMgr::Singleton().GetProgramDataMgrCPtr()->GetStrategyMapDataCPtr()->GetHeightBufCPtr();

	x = CMathFunc::Saturate(x, 0.f, (float)c_nTerrainWidth - 1);
	y = CMathFunc::Saturate(y, 0.f, (float)c_nTerrainHeight - 1);
	
	int xFloor = floor(x);
	int xCeil = ceil(x);
	int yFloor = floor(y);
	int yCeil = ceil(y);
	
	if (xCeil == xFloor)
	{
		if (yCeil == yFloor)
		{
			DWORD index = CMathFunc::XYTran2Index(xCeil, yCeil, c_nTerrainWidth, c_nTerrainHeight);
			
			return pHeightBuf[index];
		}
		else
		{
			DWORD index1 = CMathFunc::XYTran2Index(xCeil, yFloor, c_nTerrainWidth, c_nTerrainHeight);
			DWORD index2 = CMathFunc::XYTran2Index(xCeil, yCeil, c_nTerrainWidth, c_nTerrainHeight);
			return CMathFunc::Lerp<float>(pHeightBuf[index1], pHeightBuf[index2], y - yFloor);
		}
	}
	else
	{
		if (yCeil == yFloor)
		{
			DWORD index1 = CMathFunc::XYTran2Index(xFloor, yCeil, c_nTerrainWidth, c_nTerrainHeight);
			DWORD index2 = CMathFunc::XYTran2Index(xCeil, yCeil, c_nTerrainWidth, c_nTerrainHeight);
			return CMathFunc::Lerp<float>(pHeightBuf[index1], pHeightBuf[index2], x - xFloor);
		}
		else
		{
			// 三角形线性插值
			DWORD indexLD = CMathFunc::XYTran2Index(xFloor, yFloor, c_nTerrainWidth, c_nTerrainHeight);
			DWORD indexRU = CMathFunc::XYTran2Index(xCeil, yCeil, c_nTerrainWidth, c_nTerrainHeight);
			if (x > y)	// 右下三角形
			{
				DWORD indexRD = CMathFunc::XYTran2Index(xCeil, yFloor, c_nTerrainWidth, c_nTerrainHeight);
				return CMathFunc::Lerp<float>( pHeightBuf[indexLD],
											   CMathFunc::Lerp<float>(pHeightBuf[indexRD], pHeightBuf[indexRU], y - yFloor),
											   x - xFloor);
			}
			else	// 左上三角形
			{
				DWORD indexLU = CMathFunc::XYTran2Index(xFloor, yCeil, c_nTerrainWidth, c_nTerrainHeight);
				return CMathFunc::Lerp<float>( CMathFunc::Lerp<float>(pHeightBuf[indexLD], pHeightBuf[indexLU], y - yFloor),
											   pHeightBuf[indexRU],
											   x - xFloor);
			}
		}
	}
}